# 3.21.0 introduced C17 support
cmake_minimum_required(VERSION 3.21.0)
project(notcurses VERSION 3.0.13
  DESCRIPTION "Blingful UI for modern terminal emulators"
  HOMEPAGE_URL "https://nick-black.com/dankwiki/index.php/notcurses"
  LANGUAGES C)
include(CTest)
include(GNUInstallDirs)
include(CMakeDependentOption)
include(FeatureSummary)

###################### USER-SELECTABLE OPTIONS ###########################
# BUILD_TESTING is defined by CTest
option(DFSG_BUILD "DFSG build (no non-free media/code)" OFF)
option(USE_ASAN "Build with AddressSanitizer" OFF)
option(USE_COVERAGE "Assess code coverage with llvm-cov/lcov" OFF)
option(USE_CXX "Build C++ code" ON)
cmake_dependent_option(
  USE_DOCTEST "Build notcurses-tester with doctest" ON
  "BUILD_TESTING;USE_CXX" OFF
)
option(USE_DEFLATE "Use libdeflate instead of libz" ON)
option(USE_DOXYGEN "Build HTML cross reference with doxygen" OFF)
option(USE_GPM "Enable libgpm console mouse support" OFF)
option(USE_PANDOC "Build man pages and HTML reference with pandoc" ON)
option(BUILD_EXECUTABLES "Build executables" ON)
option(BUILD_FFI_LIBRARY "Build ffi library (containing all symbols which are static inline)" ON)
option(USE_POC "Build small, uninstalled proof-of-concept binaries" ON)
option(USE_QRCODEGEN "Enable libqrcodegen QR code support" OFF)
option(USE_STATIC "Build static libraries (in addition to shared)" ON)
cmake_dependent_option(
  USE_STATIC_BINARIES "Link binaries statically (requires USE_STATIC)" OFF
  "USE_STATIC" ON
)
set(USE_MULTIMEDIA "ffmpeg" CACHE STRING "Multimedia engine, one of 'ffmpeg', 'oiio', or 'none'")
set_property(CACHE USE_MULTIMEDIA PROPERTY STRINGS ffmpeg oiio none)
if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
  set(CMAKE_BUILD_TYPE RelWithDebInfo CACHE
    STRING "Choose the build mode." FORCE)
endif()
set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS Debug Release MinSizeRel RelWithDebInfo Coverage)
############## END (additional) USER-SELECTABLE OPTIONS ##################
set(USE_FFMPEG OFF)
set(USE_OIIO OFF)
if(${USE_MULTIMEDIA} STREQUAL "ffmpeg")
  set(USE_FFMPEG ON)
elseif(${USE_MULTIMEDIA} STREQUAL "oiio")
  if(NOT ${USE_CXX})
    message(FATAL_ERROR "USE_CXX must be on to use OpenImageIO.")
  endif()
  set(USE_OIIO ON)
elseif(NOT ${USE_MULTIMEDIA} STREQUAL "none")
  message(FATAL_ERROR "USE_MULTIMEDIA must be one of 'oiio', 'ffmpeg', 'none' (was '${USE_MULTIMEDIA}').")
endif()
if (NOT BUILD_EXECUTABLES AND USE_POC)
  message(WARNING "Disabling USE_POC since BUILD_EXECUTABLES=OFF")
  set(USE_POC OFF)
endif()

if(${USE_CXX})
  enable_language(CXX)
endif()
if(${USE_CXX})
set(CMAKE_CXX_EXTENSIONS OFF)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Og")
endif()
set(CMAKE_C_STANDARD 17)
set(CMAKE_C_VISIBILITY_PRESET hidden)

message(STATUS "Requested multimedia engine: ${USE_MULTIMEDIA}")
message(STATUS "Requested build mode: ${CMAKE_BUILD_TYPE}")

string(APPEND CMAKE_C_FLAGS_DEBUG " -Og")
if(${USE_COVERAGE})
  if(NOT "${CMAKE_C_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
    message(FATAL_ERROR "USE_COVERAGE was on but CC isn't clang")
  endif()
  # FIXME requires clang11+
  string(APPEND CMAKE_C_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
  if(${USE_CXX})
    if(NOT "${CMAKE_CXX_COMPILER_ID}" MATCHES "(Apple)?[Cc]lang")
      message(FATAL_ERROR "USE_COVERAGE was on but CXX isn't clang++")
    endif()
    string(APPEND CMAKE_CXX_FLAGS_DEBUG " --coverage -fprofile-instr-generate -fcoverage-mapping")
  endif()
endif()

# under msys2 (and all other operating systems) we want pkgconfig. when
# building with visual studio, don't require it.
if(NOT MSVC)
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
elseif(APPLE)
# surely there's a better way to do this? alas, seems necessary to pull the
# pkg-config files out of Homebrew.
if(NOT DEFINED ENV{PKG_CONFIG_PATH})
set(ENV{PKG_CONFIG_PATH} "/usr/local/opt/ncurses/lib/pkgconfig")
endif()
set(PKGCONFIG_DIR "${CMAKE_INSTALL_LIBDIR}/pkgconfig")
else()
set(PKGCONFIG_DIR "${CMAKE_INSTALL_PREFIX}/libdata/pkgconfig")
endif()
find_package(PkgConfig REQUIRED)
# some distros (<cough>motherfucking alpine</cough> subsume terminfo directly
# into ncurses. accept either, and may god have mercy on our souls.
pkg_search_module(TERMINFO REQUIRED tinfo>=6.1 ncursesw>=6.1)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND terminfo)
set_package_properties(terminfo PROPERTIES TYPE REQUIRED)
set(PKGCONF_REQ_PRIV "${TERMINFO_LIBRARIES}")

if(${USE_FFMPEG})
pkg_check_modules(AVCODEC REQUIRED libavcodec>=57.0)
pkg_check_modules(AVDEVICE REQUIRED libavdevice>=57.0)
pkg_check_modules(AVFORMAT REQUIRED libavformat>=57.0)
pkg_check_modules(AVUTIL REQUIRED libavutil>=56.0)
pkg_check_modules(SWSCALE REQUIRED libswscale>=5.0)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND FFMpeg)
elseif(${USE_OIIO})
pkg_check_modules(OPENIMAGEIO REQUIRED OpenImageIO>=2.1)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND OpenImageIO)
endif()
endif()

# global compiler flags
if(NOT WIN32)
# MSYS2 needs -lssp and -fstack-protector for _FORTIFY_SOURCE
# macOS with ASAN can't handle _FORTIFY_SOURCE != 1
if(NOT ${USE_ASAN})
add_compile_definitions(_FORTIFY_SOURCE=2)
endif()
add_compile_options(-Wformat -Werror=format-security)
endif()
if(MSVC)
add_compile_options(/W4)
else()
add_compile_options(-Wall -Wextra -W -Wshadow -Wvla -Wstrict-aliasing=2)
# -ffast-math dies on NaNs we draw from libav (by -ffinite-math-only)
add_compile_options(-fno-signed-zeros -fno-trapping-math -fassociative-math)
add_compile_options(-fno-math-errno -freciprocal-math -funsafe-math-optimizations)
add_compile_options(-fexceptions -fstrict-aliasing)
if(${USE_ASAN})
add_compile_options(-fsanitize=address)
add_link_options(-fsanitize=address)
endif()
endif()

# don't use REQUIRED with subsequent find_package() operations; we use
# feature_summary + set_package_properties to fail in one fell swoop.
find_package(Threads)
set_package_properties(Threads PROPERTIES TYPE REQUIRED)
# platform-specific logics
if(WIN32)
  set(LIBRT wsock32 ws2_32 secur32)
elseif(NOT APPLE)
  find_library(LIBM m REQUIRED)
  find_library(LIBRT rt REQUIRED)
endif()
if(NOT ${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
  include_directories(/usr/local/include)
  link_directories(/usr/local/lib)
  set(CMAKE_REQUIRED_INCLUDES /usr/local/include)
endif()

if(${USE_DOCTEST})
find_package(doctest 2.3.5)
set_package_properties(doctest PROPERTIES TYPE REQUIRED)
endif()

# don't cache these, or installing them requires clearing the cache to be found.
# this is going to be true for anything lacking pkg-config/CMake support.
# unigbrk.h was introduced in libunistring 0.9.4, 2010-02-14.
unset(HAVE_UNISTRING_H CACHE)
find_path(UNISTRING_INCLUDE unigbrk.h)
set(CMAKE_REQUIRED_INCLUDES ${UNISTRING_INCLUDE})
check_include_file("unigbrk.h" HAVE_UNISTRING_H)
if(NOT "${HAVE_UNISTRING_H}")
  message(FATAL_ERROR "Couldn't find unigbrk.h from GNU libunistring")
endif()
find_library(unistring unistring REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libunistring)
set_package_properties(libunistring PROPERTIES TYPE REQUIRED)

# optional dependencies lacking pkg-config support
# libdeflate has had pkgconf support since 1.9, where is it on ubuntu? FIXME
if(${USE_DEFLATE})
unset(HAVE_DEFLATE_H CACHE)
check_include_file("libdeflate.h" HAVE_DEFLATE_H)
if(NOT "${HAVE_DEFLATE_H}")
  message(FATAL_ERROR "Couldn't find libdeflate.h")
endif()
find_library(DEFLATE deflate REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND DEFLATE)
set_package_properties(DEFLATE PROPERTIES TYPE REQUIRED)
set(DEFLATE_LIBRARIES ${DEFLATE})
else()
find_package(ZLIB)
set_package_properties(ZLIB PROPERTIES TYPE REQUIRED)
endif()

if(${USE_GPM}) # no pkgconfig from gpm
unset(HAVE_GPM_H CACHE)
check_include_file("gpm.h" HAVE_GPM_H)
if(NOT "${HAVE_GPM_H}")
  message(FATAL_ERROR "Couldn't find gpm.h from libgpm")
endif()
find_library(gpm gpm REQUIRED)
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND libgpm)
set_package_properties(libgpm PROPERTIES TYPE REQUIRED)
endif()

if("${USE_QRCODEGEN}")
unset(HAVE_QRCODEGEN_H CACHE)
check_include_file("qrcodegen/qrcodegen.h" HAVE_QRCODEGEN_H)
if(NOT "${HAVE_QRCODEGEN_H}")
  message(FATAL_ERROR "USE_QRCODEGEN is active, but couldn't find qrcodegen.h")
endif()
set_property(GLOBAL APPEND PROPERTY PACKAGES_FOUND qrcodegen)
endif()

feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)

file(GLOB COMPATSRC CONFIGURE_DEPENDS src/compat/*.c)

############################################################################
# libnotcurses-core (core shared library, core static library)
file(GLOB NCCORESRCS CONFIGURE_DEPENDS src/lib/*.c)
add_library(notcurses-core SHARED ${NCCORESRCS} ${COMPATSRC})
if(${USE_STATIC})
add_library(notcurses-core-static STATIC ${NCCORESRCS} ${COMPATSRC})
else()
add_library(notcurses-core-static STATIC EXCLUDE_FROM_ALL ${NCCORESRCS} ${COMPATSRC})
endif()
# don't want these on freebsd/dragonfly/osx
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_compile_definitions(notcurses-core
  PUBLIC
   _XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
target_compile_definitions(notcurses-core-static
  PUBLIC
   _XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
endif()
set_target_properties(notcurses-core PROPERTIES
  VERSION ${PROJECT_VERSION}
  SOVERSION ${PROJECT_VERSION_MAJOR}
)
set_target_properties(notcurses-core-static PROPERTIES
  VERSION ${PROJECT_VERSION}
  OUTPUT_NAME notcurses-core
)
target_include_directories(notcurses-core
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
    "${DEFLATE_INCLUDE_DIRS}"
    "${ZLIB_INCLUDE_DIRS}"
)
target_include_directories(notcurses-core-static
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_STATIC_INCLUDE_DIRS}"
    "${DEFLATE_STATIC_INCLUDE_DIRS}"
    "${ZLIB_STATIC_INCLUDE_DIRS}"
)
target_link_libraries(notcurses-core
  PRIVATE
    "${TERMINFO_LIBRARIES}"
    "${LIBM}"
    "${unistring}"
    "${gpm}"
    "${DEFLATE_LIBRARIES}"
    "${ZLIB_LIBRARIES}"
  PUBLIC
    Threads::Threads
    "${LIBRT}"
)
target_link_libraries(notcurses-core-static
  PRIVATE
    "${TERMINFO_STATIC_LIBRARIES}"
    "${LIBM}"
    "${unistring}"
    "${gpm}"
    "${DEFLATE_STATIC_LIBRARIES}"
    "${ZLIB_STATIC_LIBRARIES}"
    Threads::Threads
    "${LIBRT}"
)
target_link_directories(notcurses-core
  PRIVATE
    "${TERMINFO_LIBRARY_DIRS}"
    "${DEFLATE_LIBRARY_DIRS}"
    "${ZLIB_LIBRARY_DIRS}"
)
target_link_directories(notcurses-core-static
  PRIVATE
    "${TERMINFO_STATIC_LIBRARY_DIRS}"
    "${DEFLATE_STATIC_LIBRARY_DIRS}"
    "${ZLIB_STATIC_LIBRARY_DIRS}"
)
if(${USE_QRCODEGEN})
target_link_libraries(notcurses-core PRIVATE qrcodegen)
target_link_libraries(notcurses-core-static PRIVATE qrcodegen)
endif()

############################################################################
# libnotcurses (multimedia shared library+static library)
file(GLOB NCSRCS CONFIGURE_DEPENDS src/media/*.c src/media/*.cpp)
add_library(notcurses SHARED ${NCSRCS} ${COMPATSRC})
if(${USE_STATIC})
# can't build binaries against static notcurses until ffmpeg linking issues
# are resolved (USE_STATIC_BINARIES) FIXME
add_library(notcurses-static STATIC ${NCSRCS} ${COMPATSRC})
else()
add_library(notcurses-static STATIC EXCLUDE_FROM_ALL ${NCSRCS} ${COMPATSRC})
endif()
set_target_properties(notcurses PROPERTIES
  VERSION ${PROJECT_VERSION}
  SOVERSION ${PROJECT_VERSION_MAJOR}
)
set_target_properties(notcurses-static PROPERTIES
  VERSION ${PROJECT_VERSION}
  OUTPUT_NAME notcurses
)
target_include_directories(notcurses
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
)
target_include_directories(notcurses-static
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
)
target_compile_definitions(notcurses
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
target_compile_definitions(notcurses-static
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
target_link_libraries(notcurses
  PUBLIC
    notcurses-core
)
target_link_libraries(notcurses-static
  PUBLIC
    notcurses-core-static
)
if(${USE_FFMPEG})
target_include_directories(notcurses
  PRIVATE
    "${AVCODEC_INCLUDE_DIRS}"
    "${AVDEVICE_INCLUDE_DIRS}"
    "${AVFORMAT_INCLUDE_DIRS}"
    "${AVUTIL_INCLUDE_DIRS}"
    "${SWSCALE_INCLUDE_DIRS}"
)
target_include_directories(notcurses-static
  PRIVATE
    "${AVCODEC_STATIC_INCLUDE_DIRS}"
    "${AVDEVICE_STATIC_INCLUDE_DIRS}"
    "${AVFORMAT_STATIC_INCLUDE_DIRS}"
    "${AVUTIL_STATIC_INCLUDE_DIRS}"
    "${SWSCALE_STATIC_INCLUDE_DIRS}"
)
target_link_libraries(notcurses
  PRIVATE
    "${AVCODEC_LIBRARIES}"
    "${AVDEVICE_LIBRARIES}"
    "${AVFORMAT_LIBRARIES}"
    "${SWSCALE_LIBRARIES}"
    "${AVUTIL_LIBRARIES}"
)
target_link_libraries(notcurses-static
  PRIVATE
    "${AVCODEC_STATIC_LIBRARIES}"
    "${AVDEVICE_STATIC_LIBRARIES}"
    "${AVFORMAT_STATIC_LIBRARIES}"
    "${SWSCALE_STATIC_LIBRARIES}"
    "${AVUTIL_STATIC_LIBRARIES}"
)
target_link_directories(notcurses
  PRIVATE
    "${AVCODEC_LIBRARY_DIRS}"
    "${AVDEVICE_LIBRARY_DIRS}"
    "${AVFORMAT_LIBRARY_DIRS}"
    "${SWSCALE_LIBRARY_DIRS}"
    "${AVUTIL_LIBRARY_DIRS}"
)
target_link_directories(notcurses-static
  PRIVATE
    "${AVCODEC_STATIC_LIBRARY_DIRS}"
    "${AVDEVICE_STATIC_LIBRARY_DIRS}"
    "${AVFORMAT_STATIC_LIBRARY_DIRS}"
    "${SWSCALE_STATIC_LIBRARY_DIRS}"
    "${AVUTIL_STATIC_LIBRARY_DIRS}"
)
elseif(${USE_OIIO})
target_include_directories(notcurses PUBLIC "${OIIO_INCLUDE_DIRS}")
target_include_directories(notcurses-static PUBLIC "${OIIO_STATIC_INCLUDE_DIRS}")
target_link_libraries(notcurses PRIVATE OpenImageIO)
target_link_libraries(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARIES})
target_link_directories(notcurses PRIVATE ${OIIO_LIBRARY_DIRS})
target_link_directories(notcurses-static PRIVATE ${OIIO_STATIC_LIBRARY_DIRS})
endif()

#######################################
# libnotcurses-ffi (ffi shared library)
if(${BUILD_FFI_LIBRARY})
file(GLOB NCFFISRCS CONFIGURE_DEPENDS src/libffi/*.c src/libffi/*.cpp)
add_library(notcurses-ffi SHARED ${NCFFISRCS})
target_compile_options(notcurses-ffi PUBLIC -fkeep-inline-functions)
target_compile_definitions(notcurses-ffi PUBLIC NOTCURSES_FFI)
# don't want these on freebsd/dragonfly/osx
if(${CMAKE_SYSTEM_NAME} STREQUAL "Linux")
target_compile_definitions(notcurses-ffi
  PUBLIC
   _XOPEN_SOURCE=700 # wcwidth(3) requires _XOPEN_SOURCE, and is in our headers
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
endif()
set_target_properties(notcurses-ffi PROPERTIES
  VERSION ${PROJECT_VERSION}
  SOVERSION ${PROJECT_VERSION_MAJOR}
)
target_include_directories(notcurses-ffi
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
    "${DEFLATE_INCLUDE_DIRS}"
    "${ZLIB_INCLUDE_DIRS}"
)

target_link_libraries(notcurses-ffi
  PRIVATE
    "${DEFLATE_LIBRARIES}"
    "${ZLIB_LIBRARIES}"
    "${TERMINFO_LIBRARIES}"
    "${LIBM}"
    "${unistring}"
    "${gpm}"
    "notcurses-core"
  PUBLIC
    Threads::Threads
    "${LIBRT}"
)

target_link_directories(notcurses-ffi
  PRIVATE
    "${TERMINFO_LIBRARY_DIRS}"
    "${DEFLATE_LIBRARY_DIRS}"
    "${ZLIB_LIBRARY_DIRS}"
)
endif()

############################################################################
if(${USE_CXX})
# libnotcurses++ (C++ wrappers)
set(NCPP_SOURCES
  src/libcpp/FDPlane.cc
  src/libcpp/Menu.cc
  src/libcpp/MultiSelector.cc
  src/libcpp/NotCurses.cc
  src/libcpp/Plane.cc
  src/libcpp/Plot.cc
  src/libcpp/Reel.cc
  src/libcpp/Root.cc
  src/libcpp/Selector.cc
  src/libcpp/Subproc.cc
  src/libcpp/Tablet.cc
  src/libcpp/Utilities.cc
  )

add_library(notcurses++ SHARED ${NCPP_SOURCES})
if(${USE_STATIC})
add_library(notcurses++-static STATIC ${NCPP_SOURCES})
else()
add_library(notcurses++-static STATIC EXCLUDE_FROM_ALL ${NCPP_SOURCES})
endif()
set_target_properties(
  notcurses++-static PROPERTIES
  OUTPUT_NAME notcurses++
)

set_target_properties(
  notcurses++ PROPERTIES
  VERSION ${PROJECT_VERSION}
  SOVERSION ${PROJECT_VERSION_MAJOR}
  OUTPUT_NAME "notcurses++")

set(NCPP_INCLUDE_DIRS
    "include"
    "src"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
  )

target_include_directories(notcurses++
  BEFORE
  PRIVATE ${NCPP_INCLUDE_DIRS}
  )

target_include_directories(notcurses++-static
  BEFORE
  PRIVATE ${NCPP_INCLUDE_DIRS}
  )

target_link_libraries(notcurses++
  PUBLIC
  notcurses)

set(NCPP_COMPILE_OPTIONS
  -Wnull-dereference
  -Wunused
  -Wno-c99-extensions
  -fno-strict-aliasing
  -ffunction-sections
  -fno-rtti
  )

set(NCPP_COMPILE_DEFINITIONS_PUBLIC
  _GNU_SOURCE _DEFAULT_SOURCE
  )

target_compile_options(notcurses++
  PRIVATE
  ${NCPP_COMPILE_OPTIONS}
  -fPIC
  )

target_compile_options(notcurses++-static
  PRIVATE
  ${NCPP_COMPILE_OPTIONS}
  -fPIE
)

target_compile_definitions(notcurses++
  PUBLIC
    ${NCPP_COMPILE_DEFINITIONS_PUBLIC}
)

target_compile_definitions(notcurses++-static
  PUBLIC
    ${NCPP_COMPILE_DEFINITIONS_PUBLIC}
)

target_compile_options(notcurses++-static
  PRIVATE
  ${NCPP_COMPILE_OPTIONS}
  -fPIE
)

target_compile_definitions(notcurses++
  PUBLIC
    ${NCPP_COMPILE_DEFINITIONS_PUBLIC}
)

target_compile_definitions(notcurses++-static
  PUBLIC
    ${NCPP_COMPILE_DEFINITIONS_PUBLIC}
)
endif() # end USE_CXX block

target_compile_options(notcurses-core
  PRIVATE
    -fPIC
)

target_compile_options(notcurses-core-static
  PRIVATE
    -fPIE
)

target_compile_options(notcurses
  PRIVATE
    -fPIC
)

target_compile_options(notcurses-static
  PRIVATE
    -fPIE
)

file(GLOB NCPP_HEADERS
  CONFIGURE_DEPENDS
  LIST_DIRECTORIES false
  ${PROJECT_SOURCE_DIR}/include/ncpp/*.hh)

file(GLOB NCPP_INTERNAL_HEADERS
  CONFIGURE_DEPENDS
  LIST_DIRECTORIES false
  ${PROJECT_SOURCE_DIR}/include/ncpp/internal/*.hh)

install(FILES ${NCPP_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp)
install(FILES ${NCPP_INTERNAL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/ncpp/internal)

export(PACKAGE notcurses)

file(GLOB NOTCURSES_HEADERS
  CONFIGURE_DEPENDS
  LIST_DIRECTORIES false
  ${PROJECT_SOURCE_DIR}/include/notcurses/*.h
  ${CMAKE_CURRENT_BINARY_DIR}/include/version.h)
install(FILES ${NOTCURSES_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/notcurses)

# tiny proofs of concept, one binary per source file
if(USE_POC)
file(GLOB POCSRCS CONFIGURE_DEPENDS src/poc/*.c)
foreach(f ${POCSRCS})
  get_filename_component(fe "${f}" NAME_WE)
  add_executable(${fe} ${f})
  target_include_directories(${fe}
    BEFORE
    PRIVATE include src "${TERMINFO_INCLUDE_DIRS}"
    "${PROJECT_BINARY_DIR}/include"
  )
  target_link_libraries(${fe}
    PRIVATE notcurses "${TERMINFO_LIBRARIES}" "${LIBM}" "${LIBRT}"
  )
  target_link_directories(${fe}
    PRIVATE "${TERMINFO_LIBRARY_DIRS}"
  )
endforeach()
if(${USE_CXX})
  file(GLOB POCPPSRCS CONFIGURE_DEPENDS src/pocpp/*.cpp)
  foreach(f ${POCPPSRCS})
    get_filename_component(fe "${f}" NAME_WE)
    add_executable(${fe} ${f})
    target_include_directories(${fe}
      BEFORE
      PRIVATE include src "${TERMINFO_INCLUDE_DIRS}"
      "${PROJECT_BINARY_DIR}/include"
    )
    target_link_libraries(${fe}
      PRIVATE notcurses++ "${TERMINFO_LIBRARIES}" "${LIBM}" "${LIBRT}"
    )
    target_link_directories(${fe}
      PRIVATE "${TERMINFO_LIBRARY_DIRS}"
    )
  endforeach()
endif()
endif()

# documentation source, processed by pandoc into XHTML and man pages. declare
# them here so that we can filter out man pages for binaries which aren't
# going to be installed.
file(GLOB MANSOURCE1 CONFIGURE_DEPENDS doc/man/man1/*.md)
file(GLOB MANSOURCE3 CONFIGURE_DEPENDS doc/man/man3/*.md)

if(BUILD_EXECUTABLES)
############################################################################
# notcurses-demo
file(GLOB DEMOSRCS CONFIGURE_DEPENDS src/demo/*.c)
add_executable(notcurses-demo ${DEMOSRCS} ${COMPATSRC})
target_compile_definitions(notcurses-demo
  PRIVATE
    _GNU_SOURCE
)
target_include_directories(notcurses-demo
  BEFORE
  PRIVATE
    include
    src
    "${TERMINFO_INCLUDE_DIRS}"
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
)
target_link_libraries(notcurses-demo
  PRIVATE
    notcurses
    ${LIBM}
    ${LIBRT}
    Threads::Threads
)

############################################################################
# notcurses-info
file(GLOB INFOSRCS CONFIGURE_DEPENDS src/info/*.c)
add_executable(notcurses-info ${INFOSRCS} ${COMPATSRC})
target_compile_definitions(notcurses-info
  PRIVATE
   _GNU_SOURCE _DEFAULT_SOURCE
)
target_include_directories(notcurses-info
  BEFORE
  PRIVATE
    src
    include
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
)
target_link_libraries(notcurses-info
  PRIVATE
    notcurses
    "${LIBRT}"
)

############################################################################
# notcurses-input
if(${USE_CXX})
file(GLOB INPUTSRCS CONFIGURE_DEPENDS src/input/input.cpp)
add_executable(notcurses-input ${INPUTSRCS})
target_include_directories(notcurses-input
  BEFORE
  PRIVATE
    include
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
)
target_link_libraries(notcurses-input
  PRIVATE
    notcurses++
    "${LIBRT}"
)

############################################################################
# nctetris
file(GLOB TETRISSRC CONFIGURE_DEPENDS src/tetris/*.cpp src/compat/*.c)
add_executable(nctetris ${TETRISSRC})
target_include_directories(nctetris
  BEFORE
  PRIVATE
    src
    include
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
)
target_link_libraries(nctetris
  PRIVATE
    notcurses++
    "${LIBRT}"
)
endif()

############################################################################
# tfman
if(NOT WIN32)
file(GLOB TFMANSRCS CONFIGURE_DEPENDS src/man/*.c)
add_executable(tfman ${TFMANSRCS} ${COMPATSRC})
target_compile_definitions(tfman
  PRIVATE
    _GNU_SOURCE
)
target_include_directories(tfman
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${DEFLATE_INCLUDE_DIRS}"
    "${ZLIB_INCLUDE_DIRS}"
)
if(USE_STATIC_BINARIES)
target_link_libraries(tfman
  PRIVATE
    notcurses-core-static
    "${DEFLATE_LIBRARIES}"
    "${ZLIB_LIBRARIES}"
)
else()
target_link_libraries(tfman
  PRIVATE
    notcurses-core
    "${DEFLATE_LIBRARIES}"
    "${ZLIB_LIBRARIES}"
)
endif()
endif()

############################################################################
# ncneofetch
file(GLOB FETCHSRCS CONFIGURE_DEPENDS src/fetch/*.c src/compat/*.c)
add_executable(ncneofetch ${FETCHSRCS} ${COMPATSRC})
target_include_directories(ncneofetch
  BEFORE
  PRIVATE
    include
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    src
)
target_link_libraries(ncneofetch
  PRIVATE
    notcurses
    "${LIBRT}"
)

# all further binaries require multimedia and C++ support
if(${USE_CXX})
if(${USE_MULTIMEDIA} STREQUAL "none")
  list(FILTER MANSOURCE1 EXCLUDE REGEX "ncls.1.md")
  list(FILTER MANSOURCE1 EXCLUDE REGEX "ncplayer.1.md")
else()
############################################################################
# ncls
file(GLOB LSSRC CONFIGURE_DEPENDS src/ls/*.cpp)
add_executable(ncls ${LSSRC})
target_include_directories(ncls
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
)
target_link_libraries(ncls
  PRIVATE
    notcurses++
)

############################################################################
# ncplayer
file(GLOB PLAYERSRCS CONFIGURE_DEPENDS src/player/*.cpp)
add_executable(ncplayer ${PLAYERSRCS} ${COMPATSRC})
target_include_directories(ncplayer
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
)
target_link_libraries(ncplayer
  PRIVATE
    notcurses++
)
endif()
endif()
else()
set(MANSOURCE1 "") # no executables were built
endif() # BUILD_EXECUTABLES

############################################################################
# testing
if(${BUILD_TESTING})
if(${USE_CXX})
#set(CMAKE_CTEST_ARGUMENTS "-V")
if(${USE_DOCTEST})
file(GLOB TESTSRCS CONFIGURE_DEPENDS src/tests/*.cpp src/compat/*.c)
add_executable(notcurses-tester ${TESTSRCS})
target_include_directories(notcurses-tester
  BEFORE
  PRIVATE
    include
    src
    "${CMAKE_REQUIRED_INCLUDES}"
    "${PROJECT_BINARY_DIR}/include"
    "${TERMINFO_INCLUDE_DIRS}"
)
target_link_libraries(notcurses-tester
  PRIVATE
    notcurses++
    "${unistring}"
    "${TERMINFO_LIBRARIES}"
)
target_link_directories(notcurses-tester
  PRIVATE
    "${TERMINFO_LIBRARY_DIRS}"
)
add_test(
  NAME notcurses-tester
  COMMAND notcurses-tester -p ${CMAKE_CURRENT_SOURCE_DIR}/data --abort-after=1
)
set_tests_properties(notcurses-tester PROPERTIES RUN_SERIAL TRUE)
install(TARGETS notcurses-tester DESTINATION bin)
else()
  list(FILTER MANSOURCE1 EXCLUDE REGEX "notcurses-tester.1.md")
endif()
endif()
enable_testing()
# the accursed Ubuntu buildd sets "TERM=unknown" for unfathomable reasons
if(DEFINED ENV{TERM} AND NOT $ENV{TERM} STREQUAL "unknown" AND USE_POC)
add_test(
  NAME notcurses-info
  COMMAND notcurses-info
)
if(${USE_CXX})
add_test(
  NAME ncpp_build
  COMMAND ncpp_build
)
add_test(
  NAME ncpp_build_exceptions
  COMMAND ncpp_build_exceptions
)
# provide an empty source
add_test(
  NAME input-devnull
  COMMAND sh -c "./notcurses-input -v < /dev/null"
)
# provide an ASCII file
add_test(
  NAME input-text
  COMMAND sh -c "./notcurses-input < ${CMAKE_SOURCE_DIR}/COPYRIGHT"
)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} DEPENDS notcurses-input)
LIST(APPEND TESTBINS ncpp_build ncpp_build_exceptions input-devnull input-text)
endif()
add_test(
  NAME sgr-direct
  COMMAND sgr-direct
)
add_test(
  NAME sgr-full
  COMMAND sgr-full
)
add_test(
  NAME rgb
  COMMAND rgb
)
LIST(APPEND TESTBINS notcurses-info sgr-direct sgr-full rgb)
if(${USE_CXX})
add_test(
  NAME rgbbg
  COMMAND rgbbg
)
LIST(APPEND TESTBINS rgbbg)
endif()
if(${USE_QRCODEGEN})
add_test(
  NAME qrcode
  COMMAND qrcode
)
endif()
set_tests_properties(
  ${TESTBINS} PROPERTIES RUN_SERIAL TRUE
)
endif()
else()
  list(FILTER MANSOURCE1 EXCLUDE REGEX "notcurses-tester.1.md")
endif()

# Pandoc documentation (man pages, HTML reference)
if(USE_PANDOC)
  find_program(PANDOC pandoc)
  if(NOT PANDOC)
    message(FATAL_ERROR "pandoc not found. USE_PANDOC=OFF to disable.")
  else()
    foreach(m ${MANSOURCE3} ${MANSOURCE1})
      get_filename_component(me ${m} NAME_WLE)
      add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${me}
        DEPENDS ${m}
        COMMAND ${PANDOC}
        ARGS --to man --standalone --from markdown-smart ${m} > ${CMAKE_CURRENT_BINARY_DIR}/${me}
        COMMENT "Building man page ${me}"
      )
      add_custom_target(${me}.man
        ALL
        DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${me}
      )
      file(GLOB ANALHTML doc/analytics-header.html)
      add_custom_command(
        OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${me}.html
        DEPENDS ${m} ${ANALHTML}
        COMMAND ${PANDOC}
        ARGS -H ${ANALHTML} --to html --standalone --from markdown-smart ${m} > ${CMAKE_CURRENT_BINARY_DIR}/${me}.html
        COMMENT "Building HTML5 ${me}.html"
      )
      add_custom_target(${me}.html5
        ALL
        DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${me}.html
      )
    endforeach()
    foreach(m ${MANSOURCE3})
      get_filename_component(me ${m} NAME_WLE)
      LIST(APPEND MANPAGES3 ${CMAKE_CURRENT_BINARY_DIR}/${me})
    endforeach()
    foreach(m ${MANSOURCE1})
      get_filename_component(me ${m} NAME_WLE)
      LIST(APPEND MANPAGES1 ${CMAKE_CURRENT_BINARY_DIR}/${me})
    endforeach()
  endif()
endif()

# Doxygen / diagrams
if(USE_DOXYGEN)
  find_package(Doxygen REQUIRED dot dia)
  if(NOT ${DOXYGEN_FOUND})
    message(FATAL_ERROR "doxygen not found. USE_DOXYGEN=OFF to disable.")
  else()
    set(DOXYFILE ${CMAKE_CURRENT_SOURCE_DIR}/doc/Doxyfile)
    # FIXME should dep on all source, i suppose, yuck
    add_custom_command(
      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/html/index.html"
      DEPENDS ${DOXYFILE}
      COMMAND Doxygen::doxygen
      ARGS ${DOXYFILE}
      COMMENT "Running doxygen"
    )
    add_custom_target(doxygen
      ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/html/index.html"
    )
    set(MODELDOT ${CMAKE_CURRENT_SOURCE_DIR}/doc/model.dot)
    add_custom_command(
      OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/model.png"
      DEPENDS ${MODELDOT}
      COMMAND Doxygen::dot
      ARGS -Tpng ${MODELDOT} -o "${CMAKE_CURRENT_BINARY_DIR}/model.png"
      COMMENT "Running dot"
    )
    add_custom_target(dot
      ALL DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/model.png"
    )
  endif()
endif()

add_custom_target(demo
  COMMAND ./notcurses-demo -p ${CMAKE_CURRENT_SOURCE_DIR}/data -c
  DEPENDS notcurses-demo
)

# pkg-config support
configure_file(tools/notcurses-core.pc.in
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses-core.pc
  @ONLY
)
configure_file(tools/notcurses.pc.in
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses.pc
  @ONLY
)

if(${BUILD_FFI_LIBRARY})
configure_file(tools/notcurses-ffi.pc.in
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses-ffi.pc
  @ONLY
)
endif()
if(${USE_CXX})
   configure_file(tools/notcurses++.pc.in
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses++.pc
  @ONLY
)
endif()
include(CMakePackageConfigHelpers)
configure_file(tools/version.h.in include/version.h)
configure_file(tools/builddef.h.in include/builddef.h)

configure_package_config_file(tools/NotcursesConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/NotcursesConfig.cmake
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Notcurses
)

write_basic_package_version_file(
  ${CMAKE_CURRENT_BINARY_DIR}/NotcursesCoreConfigVersion.cmake
  COMPATIBILITY SameMajorVersion
)

configure_package_config_file(tools/NotcursesCoreConfig.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/NotcursesCoreConfig.cmake
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/NotcursesCore
)

write_basic_package_version_file(
  ${CMAKE_CURRENT_BINARY_DIR}/NotcursesConfigVersion.cmake
  COMPATIBILITY SameMajorVersion
)

if(${USE_CXX})
configure_package_config_file(tools/Notcurses++Config.cmake.in
  ${CMAKE_CURRENT_BINARY_DIR}/Notcurses++Config.cmake
  INSTALL_DESTINATION ${CMAKE_INSTALL_LIBDIR}/cmake/Notcurses++
)

write_basic_package_version_file(
  ${CMAKE_CURRENT_BINARY_DIR}/Notcurses++ConfigVersion.cmake
  COMPATIBILITY SameMajorVersion
)
endif()

# Installation
install(FILES
  "${CMAKE_CURRENT_BINARY_DIR}/NotcursesCoreConfig.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/NotcursesCoreConfigVersion.cmake"
  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/NotcursesCore"
)

install(FILES
  "${CMAKE_CURRENT_BINARY_DIR}/NotcursesConfig.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/NotcursesConfigVersion.cmake"
  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Notcurses"
)

if(${USE_CXX})
install(FILES
  "${CMAKE_CURRENT_BINARY_DIR}/Notcurses++Config.cmake"
  "${CMAKE_CURRENT_BINARY_DIR}/Notcurses++ConfigVersion.cmake"
  DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/Notcurses++"
)
endif()

install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses-core.pc
  DESTINATION ${PKGCONFIG_DIR}
)

install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses.pc
  DESTINATION ${PKGCONFIG_DIR}
)

if(${BUILD_FFI_LIBRARY})
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses-ffi.pc
  DESTINATION ${PKGCONFIG_DIR}
)
endif()

if(${USE_CXX})
install(FILES
  ${CMAKE_CURRENT_BINARY_DIR}/notcurses++.pc
  DESTINATION ${PKGCONFIG_DIR}
)
endif()

if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
file(GLOB TESTDATA CONFIGURE_DEPENDS data/*)
# Don't install source materia for self-originated multimedia
list(FILTER TESTDATA EXCLUDE REGEX ".*xcf$")
list(FILTER TESTDATA EXCLUDE REGEX ".*osp$")
install(FILES ${TESTDATA} DESTINATION ${CMAKE_INSTALL_DATADIR}/notcurses)
endif()

install(FILES ${MANPAGES1} DESTINATION ${CMAKE_INSTALL_DATADIR}/man/man1)
install(FILES ${MANPAGES3} DESTINATION ${CMAKE_INSTALL_DATADIR}/man/man3)
file(GLOB MARKDOWN CONFIGURE_DEPENDS *.md)
list(FILTER MARKDOWN EXCLUDE REGEX "INSTALL.md")
install(FILES ${MARKDOWN} DESTINATION ${CMAKE_INSTALL_DOCDIR})

if(BUILD_EXECUTABLES)
install(TARGETS notcurses-demo DESTINATION bin)
install(TARGETS notcurses-info DESTINATION bin)
install(TARGETS ncneofetch DESTINATION bin)
if(NOT WIN32)
install(TARGETS tfman DESTINATION bin)
endif()
if(${USE_CXX})
install(TARGETS notcurses-input DESTINATION bin)
install(TARGETS nctetris DESTINATION bin)
if(NOT ${USE_MULTIMEDIA} STREQUAL "none")
install(TARGETS ncls DESTINATION bin)
install(TARGETS ncplayer DESTINATION bin)
endif()
endif()
endif() # BUILD_EXECUTABLES

if(${BUILD_FFI_LIBRARY})
LIST(APPEND INSTLIBS notcurses-ffi)
endif()
LIST(APPEND INSTLIBS notcurses-core notcurses)
if(${USE_STATIC})
LIST(APPEND INSTLIBS notcurses-core-static notcurses-static)
endif()
if(${USE_CXX})
LIST(APPEND INSTLIBS notcurses++)
if(${USE_STATIC})
LIST(APPEND INSTLIBS notcurses++-static)
endif()
endif()

install(TARGETS ${INSTLIBS}
  LIBRARY
    DESTINATION ${CMAKE_INSTALL_LIBDIR}
    COMPONENT Libraries
    NAMELINK_COMPONENT Development
)

option(DUMMY_PYTHON "Build dummy python module used for compile check and Clangd" OFF)
if(${DUMMY_PYTHON})
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory("./python")
endif()
